from driver.touch import TouchScreenHandler
from driver.usb_img import find_specific_video_devices
from driver.hsv_find import *
from driver.orbbec_img import *
import cv2,os
import numpy as np
import time
import queue
# 摄像头读取进程
# -----------------------------------------------------------------------------
def camera_reader_process(
    run_flag,             # type: ignore # 用于控制进程启停的事件
    frame_flag,           # type: ignore # 用于控制帧读取的事件
    frame_queue,       # type: ignore # 用于将帧发送出去的队列
    camera_logical_name,    # 要读取的摄像头的逻辑名称 (例如 'cap_main', 'cap_aux')
    camera_settings # 可选的相机设置，例如 {'width': 1280, 'height': 720, 'fps': 30}
):
    """
    一个独立的进程，用于从指定的摄像头读取帧并通过队列发送。

    参数:
    - run_flag: multiprocessing.Event,全局运行/停止标志。
    - frame_queue: multiprocessing.Queue,用于发送 (camera_logical_name, frame) 元组。
    - camera_logical_name: str,在 find_specific_video_devices 返回的字典中查找的键。
    - camera_settings: dict, 可选,用于设置摄像头的分辨率和FPS。
                       例如: {'width': 1280, 'height': 720, 'fps': 30}
    """

    print(f"[摄像头进程 {camera_logical_name}] 启动。")
    cap = None
    device_identifier = None #可以是路径字符串或整数索引

    # 初始相机发现
    discovered_devices = find_specific_video_devices()
    if camera_logical_name in discovered_devices:
        device_identifier = discovered_devices[camera_logical_name]
        print(f"[摄像头进程 {camera_logical_name}] 找到设备标识: {device_identifier}")
    else:
        print(f"[摄像头进程 {camera_logical_name}] 启动时未找到设备。将尝试周期性查找。")

    retry_interval = 5  # 秒，如果相机打开失败，多久后重试
    try:
        cam = OrbbecCamera(color=True, depth=True)
        cam.start()
        print("[双目摄像头进程] 启动成功。")
    except Exception as e:
        print(f"[双目摄像头进程] 启动失败: {e}")
        cam = None  # 启动失败时，cam 设为 None

    while run_flag.is_set():

        if  frame_flag.value:

            if cap is None or not cap.isOpened():
                # 如果没有找到设备标识，或者之前的尝试失败，则再次查找/尝试打开
                if device_identifier is None:
                    print(f"[摄像头进程 {camera_logical_name}] 尝试重新查找设备...")
                    discovered_devices = find_specific_video_devices()
                    if camera_logical_name in discovered_devices:
                        device_identifier = discovered_devices[camera_logical_name]
                        print(f"[摄像头进程 {camera_logical_name}] 重新找到设备标识: {device_identifier}")
                    else:
                        print(f"[摄像头进程 {camera_logical_name}] 仍未找到设备。将在 {retry_interval} 秒后重试。")
                        time.sleep(retry_interval)
                        continue # 回到 while run_flag.is_set() 的开头

                print(f"[摄像头进程 {camera_logical_name}] 尝试打开摄像头: {device_identifier}...")
        
                try:
            
                    if isinstance(device_identifier, str) and not device_identifier.startswith('/dev/video'):
                
                        try:
                            cap_id_int = int(device_identifier)
                            cap = cv2.VideoCapture(cap_id_int, cv2.CAP_V4L2) # 或者 cv2.CAP_ANY
                        except ValueError:
                            print(f"[摄像头进程 {camera_logical_name}] 无法将设备标识 '{device_identifier}' 转为整数。")
                            cap = None # 确保cap为None
                    elif isinstance(device_identifier, str): # 是 '/dev/videoX' 路径
                        cap = cv2.VideoCapture(device_identifier, cv2.CAP_V4L2) # 明确使用V4L2后端
                    else: # 假定是整数
                        cap = cv2.VideoCapture(device_identifier, cv2.CAP_V4L2)


                    if cap is None or not cap.isOpened(): # 双重检查
                        raise ConnectionError(f"无法使用标识符 {device_identifier} 打开摄像头。")

                    print(f"[摄像头进程 {camera_logical_name}] 摄像头 {device_identifier} 打开成功。")

                    # 应用相机设置 
                    if camera_settings:

                        # cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)  # 1 表示手动曝光模式 3：表示自动曝光模式
                        # cap.set(cv2.CAP_PROP_EXPOSURE, 85)  #曝光值需实验调整 
                        # cap.set(cv2.CAP_PROP_GAIN, 10)     # 增益值需实验调整

                        # 指定输出格式：MJPG = Motion-JPEG
                        #cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
                        if 'width' in camera_settings:
                            cap.set(cv2.CAP_PROP_FRAME_WIDTH, camera_settings['width'])
                        if 'height' in camera_settings:
                            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_settings['height'])
                        if 'fps' in camera_settings:
                            cap.set(cv2.CAP_PROP_FPS, camera_settings['fps'])
                        
                        # 验证设置是否生效 (可选)
                        actual_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
                        actual_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
                        actual_fps = cap.get(cv2.CAP_PROP_FPS)
                        print(f"[摄像头进程 {camera_logical_name}] 应用设置后 - 实际 Width: {actual_width}, Height: {actual_height}, FPS: {actual_fps}")


                except Exception as e: # 包括 ConnectionError
                    print(f"[摄像头进程 {camera_logical_name}] 打开摄像头 {device_identifier} 失败: {e}")
                    if cap is not None:
                        cap.release()
                    cap = None
                    device_identifier = None # 重置标识符，以便下次重新查找
                    print(f"[摄像头进程 {camera_logical_name}] 将在 {retry_interval} 秒后重试。")
                    time.sleep(retry_interval)
                    continue # 回到 while run_flag.is_set() 的开头

            # 如果摄像头已成功打开
            ret, frame = cap.read()

            if ret:
  
                try:
                    # 将帧和相机名称一起放入队列
                    # 使用 block=True 和 timeout 来避免在队列满时无限期阻塞，并允许进程响应 run_flag
                    #frame = cv2.resize(frame, (640, 480),interpolation=cv2.INTER_AREA )
                    # h, w = frame.shape[:2]
                    # roi = frame[0:h, 85:445]
                    # frame = roi  
                    frame_queue.put((camera_logical_name,frame), timeout=0.02)
                except queue.Full:
                    # time.sleep(0.01)
                    # print(f"[摄像头进程 {camera_logical_name}] 警告: 帧队列已满，丢弃一帧。")
                    continue 
                except Exception as e:
                    print(f"[摄像头进程 {camera_logical_name}] 发送帧到队列时出错: {e}")
        
            else:
                print(f"[摄像头进程 {camera_logical_name}] 无法从摄像头读取帧。可能已断开连接。")
                cap.release()
                cap = None # 触发下次循环时的重新打开逻辑
                device_identifier = None # 重置标识符
                time.sleep(1) # 短暂等待后尝试重连
                
        else:
            try:
                frame,depth = cam.read_frames()
                if frame is not None:
                    try:
                        frame_queue.put((camera_logical_name,frame), timeout=0.01)  # 非阻塞放入队列
                    except queue.Full:
                        continue
                    except Exception as e:
                        print(f"[摄像头进程 {camera_logical_name}] 双目发送帧到队列时出错: {e}")
            except Exception as e:
                print(f"[摄像头进程 {camera_logical_name}] 获取双目摄像头帧时出错: {e}")
                time.sleep(1)

    if cap is not None:
        cap.release()
    if cam is not None:
        cam.stop()
    print(f"[摄像头进程 {camera_logical_name}] 正在停止。")
    cv2.destroyAllWindows() # 虽然此进程不显示，但以防万一



TOUCH_SCREENS = {
    # 画面 1: 主界面
    1: {
        'regions': [
            {'label': 'TO_HSV', 'rect': (50, 200, 200, 270)},
            {'label': 'TO_BIN', 'rect': (50, 350, 200, 405)},
            {'label': 'RESET', 'rect': (600, 200, 750, 270)}, # 示例：重置按钮
            {'label': 'ESC', 'rect': (600, 350, 750, 405)},   # 示例：退出按钮
            # TODO: 可以添加加载配置按钮等
        ],
        'drag_sensitive_labels': [], # 主界面没有滑条
    },
    # 画面 2: HSV 调节界面
    2: {
        'regions': [
            {'label': 'SAVE_HSV', 'rect': (700, 520, 900, 590)}, # 保存按钮
            {'label': 'TO_MAIN', 'rect': (50, 520, 230, 590)},   # 返回主界面按钮
            {'label': 'CLE', 'rect': (285, 520, 430, 590)},
            {'label': 'REC', 'rect': (480, 520, 650, 590)},
            # HSV 滑条区域 (水平方向)
           
            {'label': 'h_low',   'rect': (50,  45,  450, 85)},
            {'label': 'hl_d',    'rect': (480, 45,  580, 85)},
            {'label': 'hl_i',    'rect': (640, 45,  740, 85)},

            {'label': 'h_high',  'rect': (50,  125, 450, 165)},
            {'label': 'hh_d',    'rect': (480, 125, 580, 165)},
            {'label': 'hh_i',    'rect': (640, 125, 740, 165)},

            {'label': 's_low',   'rect': (50,  205, 450, 245)},
            {'label': 'sl_d',    'rect': (480, 205, 580, 245)},
            {'label': 'sl_i',    'rect': (640, 205, 740, 245)},

            {'label': 's_high',  'rect': (50,  285, 450, 325)},
            {'label': 'sh_d',    'rect': (480, 285, 580, 325)},
            {'label': 'sh_i',    'rect': (640, 285, 740, 325)},

            {'label': 'v_low',   'rect': (50,  365, 450, 405)},
            {'label': 'vl_d',    'rect': (480, 365, 580, 405)},
            {'label': 'vl_i',    'rect': (640, 365, 740, 405)},

            {'label': 'v_high',  'rect': (50,  445, 450, 485)},
            {'label': 'vh_d',    'rect': (480, 445, 580, 485)},
            {'label': 'vh_i',    'rect': (640, 445, 740, 485)},

            {'label': 'red', 'rect': (780, 60, 920, 100)},
            {'label': 'two', 'rect': (780, 140,920 , 180)},
            {'label': 'blue', 'rect': (780, 200, 920, 240)},
            {'label': 'green', 'rect': (780, 260,920 , 300)},
            {'label': 'yellow', 'rect': (780, 320, 920, 360)},
            {'label': 'black', 'rect': (780, 380, 920, 420)},
        ],
        # 明确哪些区域是拖动敏感的滑条
        'drag_sensitive_labels': ['h_low', 'h_high', 's_low', 's_high', 'v_low', 'v_high'],
    },
    # 画面 3: BINARY 调节界面
    3: {
        'regions': [
            {'label': 'SAVE_BIN', 'rect': (750, 500, 950, 580)}, # 保存按钮
            {'label': 'TO_MAIN', 'rect': (50, 500, 250, 570)},   # 返回主界面按钮
            {'label': 'CLE', 'rect': (285, 500, 430, 570)},
            {'label': 'REC', 'rect': (480, 500, 650, 570)},
            # BINARY 滑条区域 (水平方向)
            {'label': 'thresh', 'rect': (50, 120, 450, 160)},
            {'label': 'thresh_d', 'rect':(50, 180, 450, 220)},
            {'label': 'thresh_i', 'rect':(50, 240, 450, 280)},
            {'label': 'thresh_white', 'rect': (50, 360, 450, 400)},

        ],
        'drag_sensitive_labels': ['thresh'],
    },
  
}


def hdmi_touch_task(run_flag, touch_process_flag, touch_data_queue):
    """
    触摸事件监听进程。
    run_flag: Event/Value，控制进程运行。
    touch_process_flag: Value，表示当前应处理哪个画面的触摸事件 (screen_id)。
    touch_data_queue: Queue，用于将触摸事件数据发送给主进程。
    """
    touch = TouchScreenHandler(name_substring="wch.cn CH57x", screen_width=1024, screen_height=600)

    current_screen_id = None #

    def handle_touch_event_in_region(region_index):
        """
        当在注册的区域内发生触摸事件时被 TouchScreenHandler 调用。
        将触摸信息发送到 touch_data_queue。
        """
        screen_cfg = TOUCH_SCREENS.get(current_screen_id)
        if screen_cfg and 0 <= region_index < len(screen_cfg['regions']):
            region_info = screen_cfg['regions'][region_index]
            region_label = region_info['label']
            region_rect = region_info['rect'] # 触摸区域的屏幕坐标矩形
            try:
                touch_data_queue.put(
                    (current_screen_id, region_index, region_rect, region_label, touch.last_x, touch.last_y),
                    timeout=0.001 # 短暂超时，避免队列满时阻塞
                )   
            except queue.Full:
                pass
            except Exception as e:
                 print(f"[Touch Task] Error putting data to queue: {e}")


    # 主循环，检测画面 ID 变化和处理触摸事件
    while run_flag.is_set():
        new_screen_id = touch_process_flag.value

        if new_screen_id != current_screen_id:
            print(f"[Touch Task] Screen switch detected: {current_screen_id} -> {new_screen_id}")
            if new_screen_id in TOUCH_SCREENS:
                current_screen_id = new_screen_id
                screen_cfg = TOUCH_SCREENS[current_screen_id]
                regs = [r['rect'] for r in screen_cfg['regions']]
                labels = [r['label'] for r in screen_cfg['regions']]
                cbs = {}
                drag_sensitive_indices = []

                for idx, label in enumerate(labels):
                    cbs[idx] = lambda i=idx: handle_touch_event_in_region(i)
                    if label in screen_cfg['drag_sensitive_labels']:
                        drag_sensitive_indices.append(idx)
                touch.set_buttons(regs, cbs, drag_sensitive_indices)
                print(f"[Touch Task] Rebuilt touch regions for screen {current_screen_id}: {len(regs)} regions, {len(drag_sensitive_indices)} drag sensitive.")
            else:
                print(f"[Touch Task] Received invalid screen ID: {new_screen_id}. Clearing touch regions.")
                current_screen_id = None
                touch.set_buttons([], {}, [])
        if touch.device_path: 
             touch.poll()
        else:
             time.sleep(0.1) 
        time.sleep(0.005) # 可以适当调整此延时

    # 进程退出前的清理
    print("[Touch Task] Process exiting, closing touch device.")
    touch.close()